//
//  UIImage+EffectKit.m
//  TestDemo
//
//  Created by fengxijun on 16/6/13.
//  Copyright © 2016年 goappbox. All rights reserved.
//

#import "UIImage+EffectKit.h"

static dispatch_queue_t image_dispatch_queue;

@implementation UIImage (EffectKit)

+ (void)load
{
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        image_dispatch_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    });
}

EffectImageAttributes zeroEffectImageAttributes()
{
    EffectImageAttributes zero;
    zero.width = zero.height = 0;
    zero.bitPerComponent = 8;
    zero.bitsPerPixel = 32;
    return zero;
}

- (void)effectedImage:(void(^)(unsigned char *pixels, EffectImageAttributes attributes))effectBlock
          finishBlock:(void(^)(UIImage *image))finishBlock
{
    EffectImageAttributes attributes = [self getImageAttributes];
    
    unsigned char *pixels = [self loadImageData:attributes];
    
    NSParameterAssert(effectBlock);
    effectBlock(pixels,attributes);
    
    NSParameterAssert(finishBlock);
    finishBlock([self generalImage:pixels attributes:attributes]);
}

- (UIImage *)effectedImageSync:(void(^)(unsigned char *pixels, EffectImageAttributes attributes))effectBlock
{
    __block UIImage *image = nil;
    dispatch_sync(image_dispatch_queue, ^{
        EffectImageAttributes attributes = [self getImageAttributes];
        
        unsigned char *pixels = [self loadImageData:attributes];
        
        NSParameterAssert(effectBlock);
        effectBlock(pixels,attributes);
        
        image = [self generalImage:pixels attributes:attributes];
    });
    return [image copy];
}

- (EffectImageAttributes)getImageAttributes
{
    EffectImageAttributes attr;
    CGImageRef imageRef = self.CGImage;
    attr.width = CGImageGetWidth(imageRef);
    attr.height = CGImageGetHeight(imageRef);
    attr.bitPerComponent = CGImageGetBitsPerComponent(imageRef);
    attr.bytesPerRow = CGImageGetBytesPerRow(imageRef);
    attr.bitsPerPixel = CGImageGetBitsPerPixel(imageRef);
    attr.alphaInfo = CGImageGetAlphaInfo(imageRef);
    attr.bitmapInfo = CGImageGetBitmapInfo(imageRef);
    return attr;
}

- (UIImage *)generalImage:(unsigned char *)pixels attributes:(EffectImageAttributes)attributes
{
    //下面的代码创建要输出的图像的相关参数
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, attributes.width * attributes.height * attributes.bitsPerPixel / 8, NULL);
    // prep the ingredients
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    //创建要输出的图像
    CGImageRef imageRef = CGImageCreate(attributes.width,
                                        attributes.height,
                                        attributes.bitPerComponent,
                                        attributes.bitsPerPixel,
                                        attributes.width * (attributes.bitsPerPixel / 8),
                                        colorSpaceRef,
                                        bitmapInfo,
                                        provider,
                                        NULL, NO, renderingIntent);
    
    UIImage *mosaicImage = [UIImage imageWithCGImage:imageRef];
    CFRelease(imageRef);
    CGColorSpaceRelease(colorSpaceRef);
    CGDataProviderRelease(provider);
    
    return mosaicImage;
}

- (unsigned char *)loadImageData:(EffectImageAttributes)attribute
{
    char *buffer = malloc( attribute.bitsPerPixel / 8 * attribute.width * attribute.height);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef contextRef = CGBitmapContextCreate(buffer,
                                                    attribute.width,
                                                    attribute.height,
                                                    attribute.bitPerComponent,
                                                    attribute.bytesPerRow,
                                                    colorSpace,
                                                    kCGImageAlphaPremultipliedLast);
    CGImageRef imageRef = self.CGImage;
    CGContextDrawImage(contextRef, CGRectMake(0, 0, attribute.width, attribute.height), imageRef);
    unsigned char *tmp = CGBitmapContextGetData(contextRef);
    CGContextRelease(contextRef);
    CGColorSpaceRelease(colorSpace);
    return tmp;
}

- (UIImage *)maskImage
{
    EffectImageAttributes attributes = [self getImageAttributes];
    CGDataProviderRef provider = CGImageGetDataProvider(self.CGImage);
    
    CGImageRef imageRef = CGImageMaskCreate(attributes.width, attributes.height, attributes.bitPerComponent, attributes.bitsPerPixel, attributes.bytesPerRow, provider, NULL, NO);
    
    UIImage *maskImage = [UIImage imageWithCGImage:imageRef];
    CFRelease(imageRef);
    CGDataProviderRelease(provider);
    return maskImage;
}

-(UIImage *)appendMask:(UIImage *)mask
{
    CGImageRef imageRef = CGImageCreateWithMask(self.CGImage, mask.CGImage);
    if (imageRef == nil) {
        return self;
    }
    UIImage *maskedImage = [UIImage imageWithCGImage:imageRef];
    CFRelease(imageRef);
    return maskedImage;
}

- (UIImage *)mosaicImageWithLevel:(int)level
{
    return [self effectedImageSync:^(unsigned char *pixels, EffectImageAttributes attributes) {
        
        unsigned char prev[4] = {0};
        size_t val = level;
        size_t bytewidth = attributes.width * 4;
        
        for(size_t i = 0; i < attributes.height; i++) {
            if (((i + 1) % val) == 0) {
                memcpy(pixels + bytewidth*i, pixels + bytewidth*(i-1), bytewidth);
                continue;
            }
            for(size_t j = 0;j < attributes.width; j++) {
                if (((j + 1) % val) == 1) {
                    memcpy(prev, pixels + bytewidth * i + j * 4, 4);
                    continue;
                }
                memcpy(pixels + bytewidth * i + j * 4, prev, 4);
            }
        }
    }];
}

- (UIImage *)alphaImage
{
    EffectImageAttributes attribute = [self getImageAttributes];
    
    if ([self hasAlpha:attribute]) {
        return self;
    }
    
    CGImageRef imageRef = NULL;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    CGContextRef offscreenContext = CGBitmapContextCreate(NULL, attribute.width, attribute.width,
                                                          8, 0, colorSpace, kCGImageAlphaPremultipliedLast);
    
    if (offscreenContext != NULL) {
        CGContextDrawImage(offscreenContext, CGRectMake(0, 0, attribute.width, attribute.height), self.CGImage);
        
        imageRef = CGBitmapContextCreateImage(offscreenContext);
        CGContextRelease(offscreenContext);
    }
    
    CGColorSpaceRelease(colorSpace);
    UIImage *image = [UIImage imageWithCGImage:imageRef];
    return image;
}

- (BOOL)hasAlpha:(EffectImageAttributes)attribute {
    return (attribute.alphaInfo == kCGImageAlphaFirst ||
            attribute.alphaInfo == kCGImageAlphaLast ||
            attribute.alphaInfo == kCGImageAlphaPremultipliedFirst ||
            attribute.alphaInfo == kCGImageAlphaPremultipliedLast);
}

@end
